using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using SqEngine.Audio;
using sqengine.Engine.Common;
using SqEngine;

namespace sqengine.Components
{
    public class InputBox : Microsoft.Xna.Framework.DrawableGameComponent
    {

        #region Consts
        private const int INPUT_CURSOR_DELAY = 500;
        private const string INPUT_CURSOR_CHAR = "_";
        private const string INPUT_CURSOR_PREFIX = "> ";

        private const int INPUTBOX_WIDTH = 500;
        private const int INPUTBOX_HEIGHT = 20;
        #endregion

        #region Event


        public class InputArg : System.EventArgs
        {
            private string inputtext = string.Empty;

            public InputArg(string p)
            {
                this.inputtext = p;
            }

            public string Input()
            {
                return inputtext;
            }
        }


        #endregion

        private SpriteBatch spritebatch;
        private AdvancedContent adcontent;
        private SpriteFont fnt_render;
        private string fnt_name;
        private bool visible = false;
        private string input_text = string.Empty;
        private double cursor_life;
        private string cursor_char = INPUT_CURSOR_CHAR;
        private Vector2 input_position;

        private int bbWidth;
        private int bbHeight;

        private bool canget_input = false;

        private KeysManager keyman;

        private Texture2D blank;
        private Game game;

        public delegate void InputHandler(object sender, InputArg ca);

        public event InputHandler OnClose;

        private KeyboardState prev_key;
        private KeyboardState curr_key;

        public bool IsVisible { get { return visible; } }

        public InputBox(Game game, AdvancedContent adcontent, KeysManager keyman, string font)
            : base(game)
        {
            this.adcontent = adcontent;
            this.game = game;
            this.fnt_name = font;
            this.keyman = keyman;

            keyman.OnKeyPressed += new KeysManager.KeyHandler(keyman_OnKeyPressed);

        }

        void keyman_OnKeyPressed(object sender, KeysManager.KeyArg ca)
        {
            input_text += ca.GetPressedChar;
        }

        public override void Initialize()
        {
            base.Initialize();
        }
        public void Close()
        {
            input_text = string.Empty;
            visible = false;
            canget_input = false;
            ((sqengine)game).IsInputEnabled = false;

        }

        public override void Update(GameTime gameTime)
        {
            if (visible)
            {
                cursor_life -= gameTime.ElapsedGameTime.TotalMilliseconds;

                if (cursor_life <= 0)
                {
                    if (cursor_char == "")
                        cursor_char = INPUT_CURSOR_CHAR;
                    else
                        cursor_char = "";

                    cursor_life = INPUT_CURSOR_DELAY;

                }
                if (canget_input)
                    CollectInput();

            }
            base.Update(gameTime);
        }

        public void Show(Vector2 pos)
        {
            if (!visible)
            {
                if (pos.X == -1)
                    pos.X = bbWidth / 2 - INPUTBOX_WIDTH / 2;

                if (pos.Y == -1)
                    pos.Y = bbHeight - 60;

                input_position = pos;

                input_text = string.Empty;

                visible = true;
                ((sqengine)game).IsInputEnabled = true;
            }
        }

        public void Show(int x, int y)
        {
            Show(new Vector2(x, y));
        }
        public override void Draw(GameTime gameTime)
        {
            if (visible)
            {
                spritebatch.Begin(SpriteBlendMode.AlphaBlend);

                //Draw input bar
                spritebatch.Draw(blank, new Rectangle((int)input_position.X, (int)input_position.Y, INPUTBOX_WIDTH, INPUTBOX_HEIGHT), Color.White);
                spritebatch.Draw(blank, new Rectangle((int)input_position.X + 1, (int)input_position.Y - 1, INPUTBOX_WIDTH - 1, INPUTBOX_HEIGHT - 1), Color.Black);
                spritebatch.DrawString(fnt_render, INPUT_CURSOR_PREFIX + input_text, new Vector2((int)input_position.X + 2, (int)input_position.Y + 1), Color.White);

                spritebatch.End();

                canget_input = true;

            }
            base.Draw(gameTime);
        }

        protected override void LoadContent()
        {
            spritebatch = new SpriteBatch(game.GraphicsDevice);
            fnt_render = adcontent.LoadSpriteFont(fnt_name);
            blank = adcontent.LoadTexture2D("Textures/UI/blank");

            bbWidth = GraphicsDevice.PresentationParameters.BackBufferWidth;
            bbHeight = GraphicsDevice.PresentationParameters.BackBufferHeight;

            base.LoadContent();
        }

        private void CollectInput()
        {

            prev_key = curr_key;
            curr_key = Keyboard.GetState();

            if (prev_key.IsKeyDown(Keys.Space) && curr_key.IsKeyUp(Keys.Space))
                input_text += " ";

            if (prev_key.IsKeyDown(Keys.Back) && curr_key.IsKeyUp(Keys.Back) && input_text.Length > 0)
                input_text = input_text.Remove(input_text.Length - 1, 1);

            if (prev_key.IsKeyDown(Keys.Enter) && curr_key.IsKeyUp(Keys.Enter))
            {
                if (OnClose != null)
                    OnClose(this, new InputArg(input_text));

                Close();
            }

        }
        //internal Point Convert3DPointTo2D(Vector4 _3D_point)
        //{
        //    // this function is used by me to draw a label in screen space (2D) from a certain 3D point

        //    // this function makes the following assumptions:
        //    // bd_view_matrix and bd_projection_matrix are global variables of type Matrix that contain the current view and projection matricies
        //    // bd_camera_caged is a global variable of type bool that signifies whether the camera is moving or the world is moving (in my application I can
        //    // either move the objects by either moving the world or by moving the camera)
        //    // Vector4 _3D_point is the 3D point we are wanting the screen 2D coordinates for
        //    // bd_pnl_render is the control that was binded to the Microsoft.DirectX.Direct3D Device during it's creation

        //    // is the camera moveable or are we moving the world?
        //    if (bd_camera_caged)
        //    {
        //        // camera is stationary but world is moving so we have to flip the Y axis from Vector4
        //        // we flip Y axis because from camera's viewpoint if the world moves down the camera moves up and vice versa
        //        _3D_point.Y = -_3D_point.Y;
        //    }

        //    // is this 3D point sheared away from world space (i.e. W != 1)?
        //    float W = _3D_point.W;
        //    if (W != 1)
        //    {
        //        // yep, it's sheared from world space. Let's get it back to world space since it lives in homogeneous space now
        //        // if we divide the X, Y, and Z by W we translate this point from homogeneous space to world space
        //        // set the W component to 1f to signify that we are back in world space (we don't divide the W component by W
        //        // due to float point error - we want to ensure that W is in fact 1f)
        //        _3D_point = new Vector4(_3D_point.X / W, _3D_point.Y / W, _3D_point.Z / W, 1f);
        //    }

        //    // okay, now the point lives in 3D world space. But now we have to transform it by the view and projection matricies
        //    // to get it to screen space. This transform can, and most usually will, put the 3D point back into homogeneous space
        //    // (it will be sheared from world space thus W will not equal 1f)

        //    // transform the point in 3D space by the view and projection matricies thus projecting it to 'screen space'
        //    _3D_point = Vector4.Transform(_3D_point, bd_view_matrix * bd_projection_matrix);

        //    // if the Z component if less than 0 then don't render the point (it would be behind the camera if Z < 0)
        //    // we test this Z component before even checking to see whether it lives in homogeneous space or world space because
        //    // irregardless of where it lives if Z < 0 then it's behind the camera (aka screen) and thus should not be rendered
        //    if (_3D_point.Z < 0)
        //    {
        //        // return a value that is unreasonable to signify point is behind camera (aka screen) so it can't be seen
        //        // I test for this value (yes, I know it's hard coded and should be a const variable instead) to see whether
        //        // I can render the label or not
        //        return new Point(999999999, 999999999);
        //    }

        //    // now convert the 3D point from homogeneous space to world space
        //    Vector3 result;
        //    // assign W to _3D_point's W value
        //    W = _3D_point.W;

        //    // see if we are in world space already
        //    if (W == 1)
        //    {
        //        // yep, already in world space
        //        result = new Vector3(_3D_point.X, _3D_point.Y, _3D_point.Z);
        //    }
        //    // nope, we are sheared from world space by W
        //    else
        //    {
        //        // See if W is 0 or not
        //        if (W == 0)
        //        {
        //            // we can't divide by 0 so we set W to some really small value so we can perform the division to bring the 3D
        //            // point back to world space
        //            W = 0.000001f;
        //        }

        //        // divide by W thus bringing the point back to world space
        //        result = new Vector3(_3D_point.X / W, _3D_point.Y / W, _3D_point.Z / W);
        //    }

        //    // screen coordinates for Windows start at (0,0), which is top left hand side,
        //    // and the X axis increases to the right, Y axis increases going down
        //    // to find the X,Y screen coordinate we:
        //    // take the result.X and multiply it by the binded client area's width / 2. This will give it's X coordinate in relation to window's (0,0)
        //    // coordinate system. Now to translate it to our 3D world coordinate system we have to add half the client area's width.
        //    // take the result.Y and multiply it by the binded client area's height / 2. This will give it's Y coordinate in relation to window's (0,0)
        //    // coordinate system. Now to translate it to our 3D world coordinate system we have to add half the client area's height.
        //    // The divide by 2's are needed to translate windows origin from top left corner to center screen.
        //    // We have now translated the 3D point to a 2D point on the screen based on windows (0,0) top left hand coordinate system

        //    // Output result from 3D to a screen 2D coordinate
        //    // is the camera movable or are we moving the world?
        //    if (bd_camera_caged)
        //    {
        //        // we are moving the world and camera is stationary
        //        // don't negate the Y component because it was 'flipped' already when we defined the 3D point (_3D_point)
        //        // we are taking the viewpoint of the world so if the world moves down, the point should also move down
        //        return new Point(+(int)(result.X * (bd_pnl_render.Width / 2.0f) + (bd_pnl_render.Width / 2.0f)),
        //                         +(int)(result.Y * (bd_pnl_render.Height / 2.0f) + (bd_pnl_render.Height / 2.0f)));
        //    }
        //    else
        //    {
        //        // we are moving the camera and world is stationary
        //        // negate the Y component because it was not 'flipped' when we defined the 3D point (_3D_point)
        //        // we are taking the viewpoint of the camera so if the camera moves down, the point should move up
        //        return new Point(+(int)(result.X * (bd_pnl_render.Width / 2.0f) + (bd_pnl_render.Width / 2.0f)),
        //                         -(int)(result.Y * (bd_pnl_render.Height / 2.0f) - (bd_pnl_render.Height / 2.0f)));
        //    }
        //}









    }
}